I have started building my own operating system, and I wanted to write a little about it. As Frank Herbert famously almost said, beginnings are such desperate times. The great thing about writing your own kernel is that you can say fuck that to everything. It is a sublime state of zen, to happily write down your alternative to ELF. Calling everything you've ever worked with deprecated legacy bloat, it is great fun!
So how does my OS work? Without going too much in depth on how kernels work, which we'll get to later, the basics are simple. Kind of. You turn on your computer and you boot from a disk. On the disk is a file system and a kernel. The kernel handles devices, such as your screen and your keyboard, as well as running programs. These programs can do anything. Almost anything. It is as simple as that.
So simple that microsoft can't do it right.
Where does it get hard? There isn't any trouble in loading a local descriptor table... No, the real trouble is realizing kernel development is not qualitative programming but quantitative programming. The challenge comes from realizing you need to have a global descriptor table before you can get started on a local one. The local descriptor table must be referenced in a task state segment. The task state segment must be stored in the global descriptor table. To be able to reference all available memory, you need to know what memory even exists. To do that, you will need to call BIOS function 0x15 with EAX=0xE820. But to call any bios function, you will need to be in real mode, i.e. 16-bit mode. But you don't want to be in 16 bit mode. This is the twenty-first century, thus we want long mode, i.e. 64-bit mode. So, to call a bios function, you will either need to drop into real mode real quick, or you need to detect memory before you enter long mode. And with a table of all available memory, you will need to keep track of what memory you've allocated where.
Note that me saying this is not qualitative programming does not mean I say you should write garbage.
The thing is, there are a lot of things. Some things, you can not change. Some things, you must think up for yourself. The latter are the fun parts. The parts to say fuck ELF, this is my operating system and I get to choose the format.
So how have I designed my operating system? Rule 1: forget everything you know. We've already established that.
Through which other means will I revolutionize programming, and computers in general? If we dissect UNIX, the base
of every real operating system of the last decades, it holds 3 key rules, as summarized
by Doug McIlroy. First, write programs that do one thing and do it well. Second, write programs to work together.
And third, write programs to handle text streams, because that is a universal interface. This is a great summarization
of UNIX, except it misses the ultimate design point: everything in UNIX is a file.
This is a very important consideration in building an os: what is everything. The second real operating
system of the past decades is, of course, Microsoft Windows Java and the Java Virtual Machine.
Why is Java so important? Because Java has its own everything: Java is an Object Oriented language. In
Java, everything is an object. For this article, I'll assume you already know about
OOP, as I do not have the time to explain
everything in my little explanation about the concept of everything.
What is my everything? I believe this requires a critical dissection, first, of my target audience. UNIX, influential and powerful as it was, is minimalist to such a degree, the free-roam power can no longer be used at full potential, as it requires a very deep understanding of the internal workings of your system before you are able to fully utilize your system. Enter X windows, bringing an interface with absolutely no relation to the UNIX philosophy. X makes it easier to use your computer, yes, but on the other hand, it scares you away from the terminal, from the real magic. So, on UNIX, you either know too much or too little about your system. UNIX is made for developers. Not for common people. If common people wish to use UNIX, they use X windows, which makes them afraid of UNIX.
My everything is objects. Files are too much of a technicality for any home user. Of course it can come in handy to have a hierarchical file system tree, containing everything from programs to devices to the home directory of a user. But it stays a technicality. Any real user will just want to open their document. Any developer will just want to tell the system: "Hey, I have this data. Store it pretty please." In this manner the obvious paradigm is not to transfer how data is stored but simply what data is stored, and the meaning of that data. Thus, an application describes what it can do and how that is stored. Rule 2: prioritize the home user, and make them grow into a developer. The target audience can be split into two audiences: who use a system, and who develop upon a system. On MS Windows, these are (1) everybody who doesn't care for computers and just needs something that barely works and (2) money hungry for-profit companies. On GNU/Linux, these are (1) nerds and (2) bigger nerds yet. On Linux / UNIX, you are almost forced to be a developer to even use the system. On my system, these are (1) all cool people and (2) those people if they are interested and after a few months of learning to code.
The problem with file formats, in general, is the arbitrariness of their design. An object, however, includes not only all data in an interactive structure, but in an easy structure with semantic metadata about the data.
The concept of everything includes but a few specific parts. These are:
- devices
- programs
- documents
Ultimately, devices stand atop of the responsibilities imposed upon any kernel. Devices include, but are not limited to, your storage medium of choice (disk, pendrive), your output medium of choice (display, audio), your input medium of choice (keyboard, mouse).
Then, we have programs. Programs are very important, because your kernel can not do everything. And programs are beautiful, because you give other people the ability to create something. Programs also hold a crucial position in comparison to the other two parts: you need a program to interact with a device and you need a program to use a document.
But you do not need to always use the same program to modify the same document. Storing objects as opposed to files is advantaged by the ability to use polymorphism within documents. Take, for example, an MP3. Yes, you need specialized code to interact with it, but on the other hand, it can pretty damn sure implement an interface IAudioFile, which can be used by not an MP3-player but an Audio-player. The real power of object-oriented design is dropping control over data. A program should, when installed, tell the system what it can do to what.
The beauty is, an MP4 can also implement IAudioFile. Of course, it is a video. But it also has an audio track. So, the point is, without any conversion, you can add a video to your music library. And perhaps album covers are implemented as IAudioGraphic, and your MP4 also implements this, but showing the video instead of a static cover.
Actually, the form of polymorphism I am describing is not precisely OOP, but a more sinister paradigm. I am talking about Object Composition, which is OOP on crack. An object contains one or more components, such as an audio track, an album cover, or a video stream. And these components may provide their own derivatives: a video stream can provide an album cover.
The paradigm of composition shall be extended upon all three parts of everything. A document is composed, but just as much is a program and a device. A program is composed of the entrypoints it has, of the devices it provides support for, for the classes and interfaces it brings, et cetera. A device is ultimately but an object - it, too, is composed of classes and interfaces. The kernel detects a PCI device. IPCIDevice. A driver reports it can use it as a storage device if it reports its pci-class as such-and-such and gives it IStorageDevice. Another driver detects a filesystem and gives it a IFAT32Partition. Maybe, the disk contains multiple partitions. That'll give it two IFAT32Partitions. Rule 3: flexibility is achieved through (chained) composition.